"""
Tree View
=========

Classes to implement tree view with collumns.

Started off kivy's official treeview.
Too much complexity accumulated over time...



"""

from kivy.clock import Clock # , ClockEvent
from kivy.uix.label import Label
from kivy.uix.widget import Widget,WidgetMetaclass
from kivy.uix.scrollview import ScrollView
from kivy.uix.scatter import Scatter
from kivy.uix.slider import Slider
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout


#from gridlayout import GridLayout
from kivy.uix.layout import Layout
from kivy.properties import BooleanProperty, ListProperty, ObjectProperty, \
        AliasProperty, NumericProperty, ReferenceListProperty

from kivy.graphics import Color, Line

from kivy.core.window import Window


import pdb

## My debugging -------------------------------------
import sys
class Logger(object):
    logPrefix = '%-23s '
    logFillList = []

    def debug(self, *args):
        pass
    
    def debug_on(self, *args):
        print self.logPrefix%(sys._getframe(1).f_code.co_name,)+' || ','-'.join(self.logFillList), ' '.join(unicode(a) for a in args)

    def donothing(self,*args):
        #print 'donothing'
        pass
    @classmethod
    def setDebugOn(cls,v=True):
        if v :
            cls.debug = cls.debug_on
        else:
            cls.debug = cls.donothing
            
    def setDebugPrefix(self,n):
        nc = 30 - len(n)        
        self.logPrefix = n+'.%-'+str(nc)+'s'
Logger.debug = Logger.debug_on
Logger.setDebugOn(False)

class ClassWithLoggingBase(type):
    """ A metaclass to provide logging capabilities based on Logger
    This metaclass makes it class instance inherit the Logger class"""
    def __new__(metacls, name, bases, dct):
        if bases:
            if bases[0] is object:
                bases = (Logger,)+bases[1:]
            else:
                if not any( issubclass( b , Logger) for b in bases):
                    bases = (Logger,)+bases
        else:
            bases = (Logger,)
        #print bases
        nc = 30 - len(name)
        dct['logPrefix'] = name+'.%-'+str(nc)+'s'
        dct['logFillList'] = []
        return type.__new__(metacls, name, bases, dct)
class ClassWithLogging(ClassWithLoggingBase, WidgetMetaclass): pass
##  -------------------------------------




class TreeViewException(Exception):
    """Exception for errors in the :class:`TreeView`
    """
    pass

class TreeViewBlankNode(Widget):
    pass

class TreeViewNode(Widget):
    """TreeViewNode class, used to build node class for TreeView object.

    TreeViewNode's has N children where N is the number of columns of the TreeView
    it belongs to. 
    """
    __metaclass__ = ClassWithLogging

    
    class _fake_treeview(object):
        width = 0
        _width_status=0
        def internal_x(self, dummy=0):
            return 0
        def internal_right(self, dummy=0):
            return 0
        def _request_height_layout(*dummy):
            return
        def internal_width(self, dummy=0):
            return 0
        
    treeview = _fake_treeview()

    _needs_internal_layout = True

    def __init__(self, columns = [], **kwargs):
        super(TreeViewNode, self).__init__(**kwargs)
        if not isinstance(columns, list):
            columns = [ columns ]
        self.columns = columns
        self._width_status = 0

    def setup(self, treeview):
        self.treeview = treeview
        self.clear_widgets()
        self.debug( 'setting up node .', self ,' with ', self.columns , ' num_columns',treeview.num_columns) 

        # prepare the list of children widget by interpreting the columns property.
        fullList = []
        for w in self.columns :
            w = treeview.interpret_column(w)
            w.r_color = [1,0,0,1]
            w.size_hint = (None,None)
            fullList.append(w)
        nchildren = treeview.num_columns

        # complete the list with blanks, taking into account the row_span property
        if self.has_row_span:
            nchildren -= (self.row_span[1]-1)
        for dummy in xrange(len(self.columns), nchildren):
            fullList.append(treeview.interpret_column(None))
        if len(fullList) != nchildren:
            # should use the proper error system
            print "ERROR !! in ",self,' has ',len(fullList),'children, expecting',nchildren
            return
        children = self.children
        for w in fullList:
            self.add_widget( w, len(children) ) # append at the end
            w.bind(height=self.height_changed)
        self.debug( 'setting up node  ---> ', self.children)

    def height_changed(self,wid,h):
        self.height = max(w.height for w in self.children)
        self.treeview._request_height_layout()

    def _internal_tree_xrange(self, treeview):
        return treeview._tree_container.x, treeview._tree_container.right
    
        
    def do_layout_internal(self,treeview):
        if self._width_status == treeview._width_status:
            return
        spacing_x  = treeview.spacing_x

        # compute the widths for each children
        if self.has_row_span:            
            col_width = treeview.computed_columns_width[:]
            self.debug( 'laying out spann ', col_width, ' row_span', self.row_span            )
            w = sum(col_width[self.row_span[0]: sum(self.row_span) ]) + spacing_x*(self.row_span[1]-1)
            del(col_width[self.row_span[0]+1: sum(self.row_span) ])
            col_width[self.row_span[0]] = w
            #print 'laying out spann ', col_width , w
        else:
            col_width = treeview.computed_columns_width
        
        for wid,w in zip(self.children,col_width):
            wid.width = w


        x_min , x_max = self._internal_tree_xrange(treeview)
        for i in range(len(col_width)-1,-1,-1):
            wid = self.children[i]
            w = col_width[i]
            wid.x =x_max - w
            if w!=0:
                x_max -=(spacing_x+w)

        # reposition first children
        #x_tree, y_tree = treeview.internal_position()
        #        self._place_first_column(treeview)

        #self.debug(' local tree pos ', x_min, y_tree , '  tree_container', treeview._tree_container.pos)
        x_pos0 = x_min  + treeview.indent_start + treeview.indent_level*treeview.ref_indent_level
        w0 = self.children[0]
        w0.width = w0.right - x_pos0
        w0.x = x_pos0

        height = max( w.height for w in self.children)
        self.height = height
        
        self.width = treeview._tree_container.width  - treeview.indent_start
        self._width_status = treeview._width_status

        self.debug( ' layed out with ', self, 'pos=',self.pos, 'size=', self.size, self.width, treeview.width) #, 'treeview.x ',treeview.x )
        self._needs_internal_layout = False

    def do_layout_position(self, top=None, *l):
        if top is None :
            top = self.top
        self.top = top
        height = self.height
        
        for wid in self.children:
            wid.height = height            
            wid.top = top
            

    def content_width(self,i,numCols):
        if not self.has_row_span or not( self.row_span[0] <= i < self.row_span[0] + self.row_span[1]):
            return 0 if len(self.children) < i else self.children[i].width
        # we are asked for a column below a spanning widget
        # give back the widget size divide lenght of span
        return self.children[ self.row_span[0]].width / float(self.row_span[1])

    def max_level(self):
        submax =  [ c.max_level() for c in self.nodes ]
        return max(submax+[self.level])


    def _children_iterator(self, visibility, openness):
        if not visibility(self):
            return
        yield self
        if openness(self):
            for n in self.nodes:
                for subn in n.all_children_iterator(visibility, openness):
                    yield subn
    def all_children_iterator(self, onlyopen=False, onlyvisible=False , onlynonvisible=False, onlyclosed=False):
        if onlyvisible:
            visibility = lambda self : self.is_visible
        elif onlynonvisible:
            visibility = lambda self : not self.is_visible
        else:
            visibility = lambda self : True

        if onlyopen:
            openness = lambda self : self.is_open
        elif onlyclosed:
            openness = lambda self : not self.is_open
        else:
            openness = lambda self: True
        for n in self._children_iterator(visibility, openness):
            yield n
    

    def all_children(self, onlyopen=False,onlyvisible = False):
        return [ n for n in self.all_children_iterator(onlyopen = onlyopen,onlyvisible= onlyvisible) ]

    def all_nodes_below_iterator(self,n=None,onlyopen=False,onlyvisible=False):
        if n:
            i = self.nodes.index(n)+1
        else:
            i = 0
            if not onlyvisible or self.is_visible:
                yield self
        if (not onlyopen or self.is_open) and (not onlyvisible or self.is_visible) :
            for sn in self.nodes[i:]:
                for subn in sn.all_children_iterator(onlyopen,onlyvisible):
                    yield subn

        if self.parent_node:
            for subn in self.parent_node.all_nodes_below_iterator(self,onlyopen,onlyvisible):
                yield subn


    def all_nodes_below(self,onlyopen=False, onlyvisible=False):
        return [ n for n in self.all_nodes_below_iterator(onlyopen = onlyopen, onlyvisible=onlyvisible) ]


    ## # just for debugging :
    ## def __str__(self):
    ##     if self.children == []:
    ##         return self.__repr__()
    ##     elif isinstance(self.children[0] , TreeViewBlankNode):
    ##         return self.__repr__()
    ##     else:
    ##         return self.children[0].text


    def on_is_visible(self, i, value):
        for w in self.all_children_iterator(onlyopen=True, onlyvisible=True):
            w.do_layout_internal(self.treeview)           
        self.treeview._request_height_layout()

    def on_is_open(self, i, value):
        #print 'LLLLLLLLL  is_open ', self, self._width_status, self.treeview._width_status
        for w in self.all_children_iterator(onlyopen=True, onlyvisible=True):
            w.do_layout_internal(self.treeview)            


    def remove_children_nodes(self):
        """Remove all children from the treeview """
        if self.is_leaf:
            return
        
        for node in nodes:
            node.parent_node = None
        self.nodes = []
        self.is_leaf = True

        self.treeview._request_height_layout()

    def sort_subnodes(self, col, cmpF=None, reverse=False):
        """Sort sub-nodes.
        cmpF is a comparison function (it is passed to list.sort(cmp=cmpF))
        it will be passed widgets at position col in the sub-nodes. Defaults to cmp(c1.text,c2.text)"""
        
        sortable = []
        non_sortable = []
        for n in self.nodes:
            if len(n.columns)>col and n.is_leaf :
                sortable.append(n)
            else:
                non_sortable.append(n)
        if cmpF is None:
            cmpF = lambda c1,c2 : cmp(c1.text,c2.text)
        sorterF = lambda n1, n2: cmpF(n1.columns[col],n2.columns[col])

        sortable.sort(cmp=sorterF,reverse=reverse)

        self.nodes = sortable + non_sortable
        for n in self.nodes:
            if not n.is_leaf:
                n.sort_subnodes(col, cmpF=cmpF, reverse=reverse)
        self.treeview._request_height_layout()


    def consoleDump(self,i=0):
        print i*'_', tuple( c.text for c in self.columns)
        for n in self.nodes:
            n.consoleDump(i+1)
            
    is_leaf = BooleanProperty(True)
    """Boolean to indicate if this node is a leaf or not, used to adjust
    graphical representation.

    :data:`is_leaf` is a :class:`~kivy.properties.BooleanProperty`, default to
    True, and automatically set to False when child is added.
    """

    is_open = BooleanProperty(True)
    """Boolean to indicate if this node is opened or not, in case if he contain
    children nodes. This is used for graphical representation.

    .. warning::

        This property is automatically setted by the :class:`TreeView`. You can
        read it but not write on it.

    :data:`is_open` is a :class:`~kivy.properties.BooleanProperty`, default to
    False.
    """

    is_loaded = BooleanProperty(False)
    """Boolean to indicate if this node is already loaded or not. This property
    is used only if the :class:`TreeView` use asynchronous loading.

    :data:`is_loaded` is a :class:`~kivy.properties.BooleanProperty`, default to
    False
    """

    is_selected = BooleanProperty(False)
    """Boolean to indicate if this node is selected or not. This is used for
    graphical representation.

    .. warning::

        This property is automatically setted by the :class:`TreeView`. You can
        read it but not write on it.

    :data:`is_selected` is a :class:`~kivy.properties.BooleanProperty`, default
    to False.
    """

    no_selection = BooleanProperty(False)
    """Boolean to indicate if we allow selection of the node or not.

    :data:`no_selection` is a :class:`~kivy.properties.BooleanProperty`,
    default to False
    """

    nodes = ListProperty([])
    """List of nodes. The nodes list is differents than children. Nodes
    represent a node on the tree, while children represent the widget associated
    to the current node.

    .. warning::

        This property is automatically setted by the :class:`TreeView`. You can
        read it, but not write on it.

    :data:`nodes` is a :class:`~kivy.properties.ListProperty`, default to [].
    """

    columns = ListProperty([])
    """List of  column contents.
    
    The list content will be translated into proper widget by the TreeView.
    Simple string will be translated into Label.
    If not complete, the list will be completed to the appopriate num columns by blank widget.    
    """

    parent_node = ObjectProperty(None, allownone=True)
    """Parent node. This attribute is needed because :data:`parent` can be None
    when the node is not displayed.

    .. versionadded:: 1.0.7

    :data:`parent_node` is a :class:`~kivy.properties.ObjectProperty`, default
    to None.
    """

    level = NumericProperty(-1)
    """Level of the node.

    :data:`level` is a :class:`~kivy.properties.NumericProperty`, default to -1.
    """

    color_selected = ListProperty([.3, .3, .3, .3])
    """Background color of the node when the node is selected.

    :data:`color_selected` is a :class:`~kivy.properties.ListProperty`, defaults
    to [.1, .1, .1, 1]
    """

    odd = BooleanProperty(False)
    """
    This property is set by the TreeView widget automatically. Read-only.
    :data:`odd` is a :class:`~kivy.properties.BooleanProperty`, defaults to
    False.
    """

    odd_color = ListProperty([1., 1., 1., .0])
    """Background color of odd nodes when the node is not selected.

    :data:`bg_color` is a :class:`~kivy.properties.ListProperty`, default
    to [1., 1., 1., 0.].
    """

    even_color = ListProperty([0.5, 0.5, 0.5, 0.1])
    """Background color of odd nodes when the node is not selected.

    :data:`bg_color` is a :class:`~kivy.properties.ListProperty`, default
    to [.5, .5, .5, .1].
    """

    row_span = ListProperty([])
    """If non void, the children starting at [0] will span over [1] columns.
    This treenode will thus contain treeview.num_columns-[1] children"""

    def _has_row_span(self):
        return self.row_span != []
    has_row_span = AliasProperty(_has_row_span, None)
    """ True if row_span != [] """


    is_visible = BooleanProperty(True)

    
class TreeViewHeaderNode(TreeViewNode):    
    """Special node subclass for tree header """
    sortable_columns = []
    sort_functions = []
    def setup(self,treeview):
        self.debug( "TreeViewHeaderNode setup", self.columns)
        newCol = []
        for w in self.columns:
            w = treeview.interpret_column(w)
            hw = TreeViewHeaderWidget( w)
            newCol.append(hw)
        self.columns=  newCol
        super(TreeViewHeaderNode,self).setup(treeview)
        self.treeview = treeview
        self._start_resize = None
        if self.sortable_columns == []:
            self.sortable_columns = [1]*len(newCol)
        if self.sort_functions == []:
            self.sort_functions = [None]*len(newCol)

    def on_touch_down(self, touch):
        #self.debug( ' TreeViewHeaderNode touch down ')
        for i,c in enumerate(self.children[:-1]):
            if c.is_on_right_edge(touch.x) :
                self._start_resize = i
                #self.debug( 'Start resizing ',i)
        return super(TreeViewHeaderNode, self).on_touch_down(touch)

    def on_touch_up(self, touch):
        if self._start_resize is not None:
            self.debug( ' TreeViewHeaderNode touch up pos='        )
            i = self._start_resize
            self.debug( ' resizing ',i ,' with ', touch.x -self.children[i].x)

            self.treeview._resize_one_column(i, touch.x -self.children[i].x )
            ## self.treeview.columns_width_force[i] = touch.x -self.children[i].x
            ## #self.treeview._force_compute_width = True
            ## #self.treeview._trigger_compute_width()
            ## self.treeview._request_width_recalculation()
            self._start_resize = None
        elif self.collide_point(*touch.pos):
            for i,c in enumerate(self.children):
                s = self.sortable_columns[i]
                print ' Do we sort at ',i, '  ',s
                if s!=0  and c.is_in_center(touch.x) :
                    print '  --> Sorting !'
                    #self.treeview._root.sort_subnodes(i, cmpF= self.sort_functions[i], reverse=(s==-1))
                    self.sortParentTree( i, (s==-1) )
                    self.sortable_columns[i] = -s

        return super(TreeViewHeaderNode, self).on_touch_up(touch)    


    def sortParentTree(self, column, reverse=False):
        self.treeview._root.sort_subnodes( column, cmpF= self.sort_functions[column] , reverse= reverse)


    def _internal_tree_xrange(self, treeview):
        return treeview._topcontainer.x, treeview._topcontainer.x + treeview._tree_container.width
    
    def _place_first_column(self, treeview):
        x_tree = treeview._topcontainer.x
        #self.debug(' local tree pos ', x_tree, y_tree , '  tree_container', treeview._tree_container.pos)
        x_pos0 = x_tree  + treeview.indent_start + treeview.indent_level*treeview.ref_indent_level
        w0 = self.children[0]
        w0.width = w0.right - x_pos0
        w0.x = x_pos0


class TreeViewHeaderWidget(Widget):
    """Special widgets for TreeViewHeaderNode """ 
    __metaclass__ = ClassWithLogging
    
    def __init__(self, wid, **kargs):
        self._wrapped_wid = wid
        super(TreeViewHeaderWidget, self).__init__(**kargs)
        self.debug( 'TreeViewHeaderWidget ',wid)
        self.add_widget(wid)

        #wid.bind(pos=self.pos, size=self.size)
        def pos_setter(w,pos):
            w._wrapped_wid.pos = pos
        def width_setter(w,width):
            w._wrapped_wid.width = width
        
        self.bind(pos= pos_setter, width= width_setter )
        
        self.text = 'Hwid' # temporary

    def is_on_right_edge(self,x):
        return self.right-self.edge_width <= x < self.right+self.edge_width
    def is_in_center(self,x):
        w = self.width / 3.
        return self.x+w <= x < self.right-w

    _wrapped_wid = ObjectProperty(None)
    edge_width = NumericProperty(4)
    """Size of the zone sensible to grabbing when resizing the column width """
    
class TreeViewLabel(Label):
    """Combine :class:`~kivy.uix.label.Label` and :class:`TreeViewNode` to
    create a :class:`TreeViewLabel`, that can be used as a text node in the
    tree.

    See module documentation for more information.
    """
    
# todo : min, max row height
class TreeViewBase(object):    
    """TreeView class. See module documentation for more information.

    :Events:
        `on_node_expand`: (node, )
            Fired when a node is being expended
        `on_node_collapse`: (node, )
            Fired when a node is being collapsed
    """

    __metaclass__ = ClassWithLogging

    DefaultWidClass = TreeViewLabel

    def __init__(self, **kwargs):
        self.register_event_type('on_node_expand')
        self.register_event_type('on_node_collapse')
        self.register_event_type('on_node_validated')        
        self._tree_trigger_layout  = Clock.create_trigger(self._do_layout, -1)
        self._trigger_resize_all_columns = Clock.create_trigger(self._resize_all_columns, -1)
        #self._tree_trigger_layout0 = Clock.create_trigger(self._do_layout0, -1)         
        super(TreeViewBase, self).__init__(**kwargs)
        tvlabel = TreeViewLabel(text='Root')
        for key, value in self.root_options.iteritems():
            setattr(tvlabel, key, value)


        self.debug(' kargs=', kwargs)
        # Root is special, always needed. Define it here
        self._root = TreeViewNode( **self.root_options )
        self._root.level = 0 
        self._root.is_open = True
        self._root.setup(self)
        


        self._not_touch_moved = True

        #        self.bind(  width=self._request_width_recalculation,            )
        self.bind(
            #pos=self._tree_trigger_layout0,
            width=self._trigger_resize_all_columns,
            indent_level=self._request_width_recalculation,
            indent_start=self._request_width_recalculation)

        
        self.ref_indent_level = None
        self.computed_columns_width = []

        self._need_width_layout = True
        self._need_height_layout = True
        
        #self._displayed_once = False
        self._width_status = 0
        self._force_compute_width = True

        #self._tree_container.bind(width = self._request_width_recalculation)
        self._tree_container.bind(width = self._trigger_resize_all_columns)
        self.debug( ' inited')
        self._tree_trigger_layout()



    def interpret_column(self, o ):
        """ This returns a widget to be inserted in a node, by interpreting o in function of its type.
        if o is already a Widget it is returned
        else str(o) is used tp create an instance of self.DefaultWidClass        
        """
        if isinstance(o, Widget):
            return o
        if o is None :
            return TreeViewBlankNode()
        s = unicode(o)
        w=self.DefaultWidClass(text =s  )
        self.debug( ' interpreted ', o , ' as  ',w)
        return w

    def add_node(self, node, parent=None, **kargs):
        """Add a new node in the tree.

        :Parameters:
            `node`: instance of a :class:`TreeViewNode`
                Node to add into the tree
            `parent`: instance of a :class:`TreeViewNode`, default to None
                Parent node to attach the new node
        """
        # check if the widget is "ok" for a node
        #self.debug(  ' adding node ',node ,' to ',parent)
        if not isinstance(node, TreeViewNode):
            node = TreeViewNode( columns =  node ,**kargs )        
        # create node
        if parent is None and self._root:
            parent = self._root
        if parent:
            parent.is_leaf = False
            parent.nodes.append(node)
            node.parent_node = parent
            node.level = parent.level + 1
            

        #node.bind(size=self._tree_trigger_layout)
        if parent and parent.is_open:
            widget_before = parent if len(parent.nodes) == 1 else parent.nodes[-2]
            treecontainer = self._tree_container
            try : # test if widget is above highest to be updated
                first_updated_row = treecontainer.children.index(widget_before)
            except:
                first_updated_row = len(treecontainer.children)

            if parent.is_visible:
                #self._tree_trigger_layout()
                self._request_height_layout()
                
        node.setup(self)
        node.do_layout_internal(self)
        #self._force_compute_width = True
        return node

    def remove_node(self, node):
        """Remove a node in a tree.

        .. versionadded:: 1.0.7

        :Parameters:
            `node`: instance of a :class:`TreeViewNode`
                Node to remove from the tree
        """
        # check if the widget is "ok" for a node
        if not isinstance(node, TreeViewNode):
            raise TreeViewException(
                'The node must be a subclass of TreeViewNode')
        parent = node.parent_node
        if parent is not None:
            nodes = parent.nodes
            if node in nodes:
                nodes.remove(node)
            parent.is_leaf = not bool(len(nodes))
            node.parent_node = None
            #node.unbind(size=self._tree_trigger_layout)            
            self._request_height_layout()


    def on_node_expand(self, node):
        pass

    def on_node_collapse(self, node):
        pass

    def on_node_validated(self, node):
        #self.debug( 'VVVVVVVVVV  node validated ! ', node )
        pass

    def select_node(self, node):
        """Select a node in the tree
        """
        if self.none_selectable or node.no_selection:
            return
        if self._selected_node:
            self._selected_node.is_selected = False
        node.is_selected = True
        self._selected_node = node

        # request keyboard should happen ONLY if a real keyboard exist:
        self._keyboard = Window.request_keyboard(
            self._keyboard_closed, self)
        self._keyboard.bind(on_key_down=self._on_keyboard_down)
        


    def open_sub_nodes(self, node):
        node.is_open = True
        for n in node.all_children_iterator():
            n.is_open = True
        self._request_height_layout()

    def toggle_node(self, node):
        """Toggle the state of the node (open/collapse)
        """
        node.is_open = not node.is_open
        self.node_changed_state(node)
        self._request_height_layout()

        
    def node_changed_state(self, node):
        if node.is_open:
            if self.load_func and not node.is_loaded:
                self._do_node_load(node)
            self.dispatch('on_node_expand', node)
        else:
            self.dispatch('on_node_collapse', node)
        #self._first_updated_row = self._tree_container.children.index(node)
        #self.debug( ' toggled ', self._first_updated_row)

    def get_node_at_pos(self, pos, imin=0, imax=-1):
        """Dichotomic search of index of the node at pos  """
        if imax == -1: imax = len(self._tree_container.children)-1
        if imin == imax :
            tc = self._tree_container
            if tc.x <= pos[0] <= tc.right and tc.y <= pos[1] <= tc.top :
                return tc.children[imin]
            else:
                return None
        inew = (imax + imin)/2
        if inew == imin : 
            c = self._tree_container.children[imax]
        else:
            c = self._tree_container.children[inew]
        y = pos[1]
        if y > c.top : return self.get_node_at_pos(pos,imin,inew)
        else:
            if inew==imin: inew = imin+1
            return self.get_node_at_pos(pos,inew,imax)
        


    def iterate_open_nodes(self, node=None):
        """Generator to iterate over expanded nodes.
        Example for get all the node opened:
        ::

            treeview = TreeView()
            # ... add nodes ...
            for node in treeview.iterate_open_nodes():
                print node

        """
        if not node:
            node = self.root
        if self.hide_root and node is self.root:
            pass
        else:
            yield node
        if not node.is_open:
            return
        f = self.iterate_open_nodes
        for cnode in node.nodes:
            for ynode in f(cnode):
                yield ynode




    def available_width(self):
        totalWidth = self._tree_container.width
        if self.ref_indent_level is None:
            self.ref_indent_level = self.root.max_level()
                    
        return totalWidth - ( (self.num_columns-1)*self.spacing_x+self.ref_indent_level*self.indent_level+self.indent_start)


    def _initial_width_computation(self, *l):

        pass

    def _compute_col_width(self, *largs):
        self.debug( self, ' computing col_width  from ',self.computed_columns_width)
        self.debug( ' computing col_width with force ',self.columns_width_force)

        num_columns = self.num_columns
        if len(self.columns_width_force) < num_columns:
            self.columns_width_force.extend( [None]*( num_columns - len(self.columns_width_force) ) )

        available_width = self.available_width()
        if available_width == 0:
            return

        available_per_col = float(available_width) / num_columns
        self.debug( ' compute col width total width', self.width,' tree_container width',self._tree_container.width, ' available_width', available_width, ' per col=',available_per_col)
        natural_width = [0]*num_columns
        for n in self.root.all_children_iterator():
            if not n.is_leaf :
                continue
            #self.debug( 'node ',n , '  widths= ',n.children[0].width, n.children[1].width
            for c in xrange(num_columns):
                natural_width[c] = max(natural_width[c],n.content_width(c,num_columns) )

        self.debug( ' computing num_columns ... natural_width       ',natural_width)
        # we have 'natural width', check if they fit in the tree width
        # start with small ones, leave them as they are.
        non_forced_width = [0]*num_columns
        big_natural_width = []
        for i,w in enumerate(natural_width):
            if 0 < w < available_per_col:
                non_forced_width[i] = w
            else:
                big_natural_width.append( [i,w] )
        self.debug( ' computing num_columns ... non_forced_width       ',non_forced_width)
        self.debug( ' computing num_columns ... big_natural_width       ',big_natural_width                )
        available_for_big = available_width - sum(non_forced_width)
        if len(big_natural_width)>0 :
            available_for_big_percol = float(available_for_big) / len(big_natural_width)
            self.debug( 'available_for_big_percol  ',available_for_big_percol)
            for l in big_natural_width:
                self.debug( ' in big_natural_width', l)
                # if w == 0, set it to available_for_big_percol
                if l[1] == 0: l[1] = available_for_big_percol
            self.debug( ' in big_natural_width', l)
            self.debug( ' computing num_columns ... big_natural_width       ',big_natural_width                            )
            # now scale evry remaining col so they fit within tree width
            frac_for_big = available_for_big *1.0 / sum( b[1] for b in big_natural_width)
            for i,w in big_natural_width:
                #self.debug( i ,w , frac_for_big)
                non_forced_width[i] = w*frac_for_big
        else:
            f = float(available_width) / sum( non_forced_width)
            for i,w in enumerate(non_forced_width):
                non_forced_width[i] = f*w

        # done with natural_width
        # confront with forced with given by user

        forced_width = [0]*num_columns
        #non_forced_width = []
        self.debug( ' columns_width_force ',self.columns_width_force)
        for i in range(num_columns):
            wf = self.columns_width_force[i]
            if wf is not None:
                non_forced_width[i]=0
                if wf>1 :
                    forced_width[i] = int(wf)
                else:
                    forced_width[i] = int(available_width*wf)

                
        forced_width_tot = sum(forced_width)
        non_forced_width_tot = sum(non_forced_width)

        # We make sure forced with are not more than 90% of the width or of the space left
        threshold =  max( 0.9*available_width, available_width - non_forced_width_tot)
        if forced_width_tot > threshold:
            r = threshold / forced_width_tot
            for i in range(num_columns):
                forced_width[i] *= r
            forced_width_tot = int(threshold)

        # finally rescale non_forced widths to fit in the remaining space
        remaining_width = available_width - forced_width_tot

        if non_forced_width_tot>0:
            r = float(remaining_width) / non_forced_width_tot 
            for i in range(num_columns):
                non_forced_width[i] *= r
            non_forced_width_tot = remaining_width

        self.debug( ' computing num_columns ... forced_width       ',forced_width)
        self.debug( ' computing num_columns ... non_forced_width   ',non_forced_width        )

        computed_columns_width = [ int(nf+f) for (nf,f) in zip( non_forced_width, forced_width)  ]
        # redistributed rounding rest if any
        for i in range( int(available_width- sum(computed_columns_width) )):
            computed_columns_width[i% num_columns] +=1

        
        
        if computed_columns_width != self.computed_columns_width:
            self._tree_trigger_layout()


        if (computed_columns_width != self.computed_columns_width):
            self._width_status +=1
        self.computed_columns_width = computed_columns_width
        self._force_compute_width = False
        self.debug( ' FINAL Computed widths =  ',self.computed_columns_width  ,' updated ==', self._width_status  )

    def _resize_one_column(self, i , newwidL):
        colW = self.computed_columns_width
        oldwidL = colW[i]
        if newwidL == oldwidL:
            return
        oldwidR = colW[i+1]

        newwidR = oldwidR+oldwidL - newwidL

        colW[i] = newwidL
        colW[i+1] = newwidR

        av_wid = self.available_width()


        self._width_status +=1

        self._request_width_layout()

    def _resize_all_columns(self, *l):        
        self.debug( ' computed_columns_width ', self.computed_columns_width, self)
        self.debug( ' _tree_container ',self._tree_container.width, ' self ', self.width)
        if self.computed_columns_width == [] :
            self._compute_col_width()
        oldwidth = sum( self.computed_columns_width)
        if oldwidth==0:
            self.computed_columns_width = [ 1 for i in self.computed_columns_width]
            return
        ## if self.available_width() == 1 :
        ##     pdb.set_trace()
        
        frac = self.available_width() / float(oldwidth)
        self.debug( ' frac ', frac, ' av', self.available_width(), ' tree_container.widt',self._tree_container.width )
        self.computed_columns_width = [ int(frac*w) for w in self.computed_columns_width]
        self._width_status +=1



        self._request_width_layout()
            
        
        
        
    #
    # Private
    #
    def on_load_func(self, instance, value):
        if value:
            Clock.schedule_once(self._do_initial_load)

    def _do_initial_load(self, *largs):
        if not self.load_func:
            return
        self._do_node_load(None)

    def _do_node_load(self, node):
        gen = self.load_func(self, node)
        if node:
            node.is_loaded = True
        if not gen:
            return
        for cnode in gen:
            self.add_node(cnode, node)

    def on_root_options(self, instance, value):
        if not self.root:
            return
        for key, value in value.iteritems():
            setattr(self.root, key, value)



    def tree_container_top(self):
        return self._tree_container.top

    def _do_layout0(self, *largs):

        self._do_layout(*largs)
        treecontainer = self._tree_container
        children = treecontainer.children


    def _request_width_recalculation(self,*l):
        self._force_compute_width = True
        self._request_width_layout()
        
    def _request_width_layout(self, *l):
        self._need_width_layout = True
        self._tree_trigger_layout()

        
    def _request_height_layout(self, *l):
        self._need_height_layout = True
        self._tree_trigger_layout()


    def _do_layout_width(self, treecontainer, visible_nodes):
        x_start= treecontainer.x + self.indent_start
        for w in visible_nodes:
            w.x = x_start
            w.do_layout_internal(self)
        self._need_width_layout = False
        
    def _do_layout_height(self, treecontainer, visible_nodes):
        spacing_y = self.spacing_y
        totHeight = sum( (w.height + spacing_y ) for w in visible_nodes)

        treecontainer.clear_widgets()
        children = treecontainer.children

        self._tree_container.height = totHeight
        
        #self.minimum_height = totHeight
        start_y = self.tree_container_top()
        count = 0
        for node in visible_nodes: #self.root.all_children_iterator(onlyopen=True):
            node.odd = False if count % 2 else True
            count += 1
            treecontainer.add_widget(node, len(children))
            self.debug(' layout of ',node, ' at ', start_y)
            node.do_layout_position(start_y)            
            start_y -= (node.height + spacing_y)
        self._need_height_layout = False

    def _do_layout(self, *largs):
        self.debug(' _force_compute_width ', self._force_compute_width, ' _need_height_layout', self._need_height_layout, ' _need_width_layout',self._need_width_layout)
        if self._force_compute_width :
            self._compute_col_width()
        
        visible_nodes = self._root.all_children(onlyvisible =True, onlyopen= True)

        if visible_nodes[0] is self._root and self.hide_root:
            visible_nodes.pop(0)

        treecontainer = self._tree_container
        if self._need_width_layout:
            self._do_layout_width(treecontainer, visible_nodes)
        if self._need_height_layout:
            self._do_layout_height(treecontainer, visible_nodes)

        self.debug( ' _do_layout end  ',self.size , self._tree_container.size)
        self.debug( ' _do_layout final all children ', len(treecontainer.children) , ' *****************************'                )
        
        
        
        
        
    def _do_open_node(self, node):
        if self.hide_root and node is self.root:
            height = 0
        else:
            self.add_widget(node)
            height = node.height
            self.debug( ' opening node ',node, height)
            if not node.is_open:
                return height
        for cnode in node.nodes:
            height += self._do_open_node(cnode)
        return height


    def internal_x(self, dummy=0):
        return self._tree_container.x

    def internal_width(self, dummy=0):
        return self._tree_container.width

    def internal_right(self, dummy=0):
        return self._tree_container.right

    def internal_position(self):
        return self.x, self.y

    def local_postion(self,pos):
        return pos
    
    
    def on_touch_up(self, touch):
        #print '___ XXXX  on_touch_up ', touch.pos , self._not_touch_moved, self# , "  ", self.local_postion(touch.pos)
        if not self._tree_container.collide_point(*self.local_postion(touch.pos)):
            print ' NOT colliding ', self
            return False
        doSelection = self._not_touch_moved and not self.none_selectable and bool(self._tree_container.children)
        doSelection = doSelection and ( 'button' in touch.profile and 'scroll' not in touch.button)
        #print ' ___ uu ', doSelection, self._not_touch_moved, not self.none_selectable, bool(self._tree_container.children)
        #print ' ___ uu ', doSelection, 'button' in touch.profile , 'scroll' not in touch.button 
        self._not_touch_moved = True
        self.debug(' bla ',doSelection, self)
        #pdb.set_trace()
        if doSelection :
            node = self.get_node_at_pos(self.local_postion(touch.pos))
            self.debug( "treviewbase touch_up node=", node) # ,node, ' pos=',touch.pos, touch.button)
            if not node:
                return super(TreeViewBase,self).on_touch_up(touch)
            # toggle node or selection ?
            #if node.x - self.indent_start <= touch.x < node.x:
            tc_x = self._topcontainer.x
            if tc_x  <= touch.x < tc_x + self.indent_start:
                self.toggle_node(node)
            elif node.x <= touch.x:
                self.select_node(node)
                #return super(TreeViewBase,self).on_touch_up(touch)
                node.dispatch('on_touch_up', touch)
                self.dispatch('on_node_validated', node)                

        super(TreeViewBase,self).on_touch_up(touch)
        return True # stop propagation


    def _keyboard_closed(self):
        self._keyboard.unbind(on_key_down=self._on_keyboard_down)
        self._keyboard = None

    def _on_keyboard_down(self, keyboard, keycode, text, modifiers):
        if self._selected_node is None:
            return True
        key, key_str = keycode

        index = -1
        childrennode = self._tree_container.children
        if key == 273: # 'cursor_up',
            index = childrennode.index(self._selected_node) - 1
        elif key == 274: # 'cursor_down',
            index = childrennode.index(self._selected_node) + 1
        elif key == 13 : # enter
            self.dispatch('on_node_validated', self._selected_node)
            return True
        if index<0 or index>len(childrennode)-1:
            return True

        self.select_node( childrennode[index] )
        return True

    # down
    #
    # Private properties
    #
    _root = ObjectProperty(None)

    _selected_node = ObjectProperty(None)

    #
    # Properties
    #

    minimum_width = NumericProperty(0)
    """Minimum width needed to contain all childrens.

    .. versionadded:: 1.0.9

    :data:`minimum_width` is a :class:`kivy.properties.NumericProperty`, default
    to 0.
    """

    minimum_height = NumericProperty(0)
    """Minimum height needed to contain all childrens.

    .. versionadded:: 1.0.9

    :data:`minimum_height` is a :class:`kivy.properties.NumericProperty`,
    default to 0.
    """

    minimum_size = ReferenceListProperty(minimum_width, minimum_height)
    """Minimum size needed to contain all childrens.

    .. versionadded:: 1.0.9

    :data:`minimum_size` is a :class:`~kivy.properties.ReferenceListProperty` of
    (:data:`minimum_width`, :data:`minimum_height`) properties.
    """

    spacing_x = NumericProperty(0)
    """Spacing along x between widgets , in pixels.

    :data:`spacing` is a :class:`~kivy.properties.NumericProperty`, default to
    0.
    """

    spacing_y = NumericProperty(0)
    """Spacing along y between widgets , in pixels.

    :data:`spacing` is a :class:`~kivy.properties.NumericProperty`, default to
    0.
    """


    indent_level = NumericProperty(16)
    """Width used for identation of each level, except the first level.

    Computation of spacing for eaching level of tree is::

        :data:`indent_start` + level * :data:`indent_level`

    :data:`indent_level` is a :class:`~kivy.properties.NumericProperty`,
    default to 16.
    """

    indent_start = NumericProperty(24)
    """Indentation width of the level 0 / root node. This is mostly the initial
    size to put an tree icon (collapsed / expanded). See :data:`indent_level`
    for more information about the computation of level indentation.

    :data:`indent_start` is a :class:`~kivy.properties.NumericProperty`,
    default to 24.
    """

    hide_root = BooleanProperty(False)
    """Use this property to show/hide the initial root node. If True, the root
    node will be always considerate as an open node

    :data:`hide_root` is a :class:`~kivy.properties.BooleanProperty`, default to
    False.
    """

    #columns_width = ListProperty( [None] )
    """ The columns width indication. contains number in [0,1] representing a fraction of tree width
    or integer >1 representing a width in pixels or None to let the tree guess the best width"""


    columns_width_force = ListProperty( [] )
    """Indicate whether to force the column width i to be the one given by columns_width[i]. """


    ## def num_num_columns(self):
    ##     return len(self.columns_width)


    num_columns = NumericProperty(0)
    """Number of collumns """


    def get_selected_node(self):
        return self._selected_node

    selected_node = AliasProperty(get_selected_node, None,
                                  bind=('_selected_node', ))
    """Node selected by :func:`TreeView.select_node`, or by touch.

    :data:`selected_node` is a :class:`~kivy.properties.AliasProperty`, default
    to None, and is read-only.
    """

    def get_root(self):
        return self._root

    root = AliasProperty(get_root, None, bind=('_root', ))
    """Root node.

    By default, the root node widget is a :class:`TreeViewLabel`, with the label
    'Root'. If you want to change the default options passed to the widget
    creation, you can use :data:`root_options` property::

        treeview = TreeView(root_options={
            'text': 'Root directory',
            'font_size': 15})

    :data:`root_options` will change the properties of the
    :class:`TreeViewLabel` instance. However, you cannot change the class used
    for root node yet.

    :data:`root` is a :class:`~kivy.properties.AliasProperty`, default to
    None, and is read-only. However, the content of the widget can be changed.
    """

    root_options = ObjectProperty({"columns":["ROOT"]})
    """Default root options to pass for root widget. See :data:`root` property
    for more information about the usage of root_options.

    :data:`root_options` is a :class:`~kivy.properties.ObjectProperty`, default
    to {}.
    """

    load_func = ObjectProperty(None)
    """Callback to use for asynchronous loading. If set, the asynchronous will
    be automatically done. The callback must act as a Python generator function
    : use yield to send data back to the treeview.

    The callback should be in the format::

        def callback(treeview, node):
            for name in ('Item 1', 'Item 2'):
                yield TreeViewLabel(text=name)

    :data:`load_func` is a :class:`~kivy.properties.ObjectProperty`, default to
    None.
    """

    none_selectable = BooleanProperty(False)
    """True to render ALL node unselectable

    :data:`no_selection` is a :class:`~kivy.properties.BooleanProperty`,
    default to False
    """


    _tree_container = ObjectProperty(None)
    

class WidgetWithMinSize(Widget):
    minimum_width = NumericProperty(0)
    """Minimum width needed to contain all childrens.

    .. versionadded:: 1.0.9

    :data:`minimum_width` is a :class:`kivy.properties.NumericProperty`, default
    to 0.
    """

    minimum_height = NumericProperty(0)
    """Minimum height needed to contain all childrens.

    .. versionadded:: 1.0.9

    :data:`minimum_height` is a :class:`kivy.properties.NumericProperty`,
    default to 0.
    """

    minimum_size = ReferenceListProperty(minimum_width, minimum_height)
    """Minimum size needed to contain all childrens.

    .. versionadded:: 1.0.9

    :data:`minimum_size` is a :class:`~kivy.properties.ReferenceListProperty` of
    (:data:`minimum_width`, :data:`minimum_height`) properties.
    """


    
        
class TreeViewWithHeaderBase(TreeViewBase):

    def __init__(self,  **kargs):
        self.debug( ' header base kargs',kargs)
        super(TreeViewWithHeaderBase, self).__init__(**kargs)
        while len(self.header_content)<self.num_columns :
            self.header_content.append(None)

        header = TreeViewHeaderNode( columns = self.header_content,size_hint=(1,None))
        header.level = 0 
        header.is_open = True
        header.setup(self)        
        self.header = header

        self.resize_line = None

        # make sure the content of the header is reset when the header moves (due to resizing, for example)
        self.header.bind(pos = lambda *l : self.header.do_layout_position(None))

    def _layout_header(self, *largs):

        if self._force_compute_width:
            self._compute_col_width()
        self.header.do_layout_internal(self)
        self.header.do_layout_position(self._topcontainer.top)
        self.debug(' done layout header ',self.header.height)
        if self.header not in self._topcontainer.children:
            tcontainer = self._topcontainer
            children  =  tcontainer.children[:]
            tcontainer.clear_widgets()
            tcontainer.add_widget(self.header)
            for c in children:
                tcontainer.add_widget(c)
            


    def _do_layout(self, *largs):
        self.debug(" in TreeViewWithHeaderBase")
        #self._topcontainer.remove_widget(self.header)        
        if not self.hide_header:
            self._layout_header(*largs)
        super(TreeViewWithHeaderBase,self)._do_layout(*largs)


    def on_touch_up(self, touch):
        #self.debug( 'TreeViewWithHeaderBase ',self.header._start_resize)        
        if self.hide_header or not self.header.collide_point(*touch.pos):
            self.debug('Not colliding ',touch.pos)
            super(TreeViewWithHeaderBase,self).on_touch_up(touch)
            return False
        else:
            self.header.dispatch('on_touch_up',touch)
            if self.resize_line :self.resize_line.points = []            
        return True
    
    def on_touch_down(self, touch):
        self.debug( 'TreeViewWithHeaderBase ')
        if  self.hide_header or not self.header.collide_point(*touch.pos):
            self.debug('Not colliding ',touch.pos)
            super(TreeViewWithHeaderBase,self).on_touch_down(touch)
            return False
        else:
            self.header.dispatch('on_touch_down',touch)            
        return True


    def on_touch_move(self, touch):
        #self.debug(" motion ",self.header._start_resize)
        if self.header._start_resize is not None:
            if self.resize_line is None:                
                with self.canvas:
                    Color(*self.resize_line_color)
                    self.resize_line = Line(points=[touch.x, self.top,touch.x,self.y])
            else:
                self.resize_line.points=[touch.x, self.top,touch.x,self.y]
        super(TreeViewBase,self).on_touch_move(touch)
        return True

            
                
    header_content = ListProperty( [] )
    """The content of the header """

    hide_header = BooleanProperty(False)
    """Hide or show header """


    resize_line_color = ListProperty([1,1,1])

def property_bind( w_change, w_update, *props):
    def mysetter(w,p):
        return lambda wsource,v :setattr(w,p,v)
            
    d = dict( (p, mysetter(w_update,p) ) for p in props)
    w_change.bind(**d)
    # set props wright now:
    for p in props:
        setattr(w_update, p , getattr(w_change,p) )



class ScrollableTreeViewBase(TreeViewBase):
    """
    Expects a _scroll_container member being a BoxLayout."""
    def __init__(self, **kargs):
        self.debug( ' scroll base kargs',kargs)
        super(ScrollableTreeViewBase, self).__init__(**kargs)
        self.slider   = Slider(orientation='vertical',size_hint=(None,1))
        self.scroller = ScrollView(size_hint=(1,1))
        self.scatter = Scatter(size_hint=(1,None))
        self.scatter.do_rotation = False
        self.scatter.do_scale = False
        self.scatter.do_translation = (False, False)

        

    def post_init(self):
        """Called at the end of __init__ when we are sure all container are properly defined"""
        _scroll_container = self._scroll_container

        if self.show_slider:            
            if self.slider_pos:
                _scroll_container.add_widget(self.slider)                
                _scroll_container.add_widget(self.scroller)
            else:
                _scroll_container.add_widget(self.scroller)
                _scroll_container.add_widget(self.slider)                
        else:
            _scroll_container.add_widget(self.scroller)


        self.scroller.add_widget(self.scatter )
        self.scatter.add_widget( self._tree_container)
        self.scatter.pos = self.scroller.pos

        ## self.scatter.bind(pos = mpos_changed)
        ## self.scroller.bind(pos = mpos_changed)        

        if self.show_slider:
            self._layout_slider()

        # bind together the slider and the scroller area
        self.slider.value_normalized = 1.
        def _f(i,v):
            if self._scroller_changed:
                self._scroller_changed = False
            else:
                self.scroller.scroll_y =v
                self._slider_changed = True
        self.slider.bind(value_normalized = _f )

        def _f2(i,v):
            if v>1. or v <0:
                return
            if self._slider_changed:
                self._slider_changed = False
            else:
                self.slider.value_normalized =v
                self._scroller_changed = True
        self.scroller.bind(scroll_y = _f2 )
        self._scroller_changed = False
        self._slider_changed = False
        ## --------------------------------------

        # make sure the _tree_container is always big enough to contain all nodes
        def tree_min_height(instance , v):
            #self._tree_container.height = v
            self.scatter.height = v
        #self.bind(minimum_height=tree_min_height)
        self._tree_container.bind(height=tree_min_height)
        def _ff(o,v):
            self._tree_container.width = v
        self.scatter.bind( width = _ff )

        self._request_width_recalculation()
        #self._scroll_container.bind(width=self._request_width_recalculation)
        self._scroll_container.bind(width=self._trigger_resize_all_columns)
        self._scroll_container.bind(width=self._layout_slider)    


        # bind touch_move so it doesn't select node when moving:
        def touch_moved(*l):
            self._not_touch_moved = False
            return False
        #self.scroller.bind(scroll_y = touch_moved)
        self.scroller.bind(on_touch_move = touch_moved)

        
        self._do_layout_containers()

        self.debug(" Width :: scroller ", self.scroller.width, '  scroll_container ',self._scroll_container.width, '  tree',self._tree_container.width)

        
    def _layout_slider(self, *largs):
        container = self._scroll_container
        self.scroller.width = container.width - self.slider.width
        self.scatter.width = self.scroller.width

    def _do_layout_containers(self, *largs):
        if self.show_slider:
            self._layout_slider(*largs)
        else:
            self.scroller.width = self._scroll_container.width
        self.scatter.width = self.scroller.width
        #self._tree_container.width = self.scatter.width
        self.debug(' --> ', self._tree_container.width, self._scroll_container.width, self.width)
        
    def _do_layout(self, *largs):
        self.debug(" in ScrollableTreeViewBase")
        self._do_layout_containers(*largs)
        super(ScrollableTreeViewBase,self)._do_layout(*largs)

    def internal_position(self):
        return 0,0
    def local_postion(self,pos):
        self.debug('  position ', pos, ' from scatter ' , self.scatter.to_local(*pos),self.scroller.to_local(*pos), ' scatter pos / self pos', self.scatter.pos, self.pos)
        #print ' ;;;;;  ', self.scatter
        return self.scroller.to_local(*pos)

    def tree_container_top(self):
        return self.scatter.height


    def select_node(self,node):
        super(ScrollableTreeViewBase,self).select_node(node)
        #print ' iiiiiiiii ScrollableTreeViewBase', node, self._selected_node
        if not self._selected_node:
            return
        node_y = self._selected_node.y
        ## if node_y+self.scatter.y < self.scroller.y+1 or \
        ##        node_y+self.scatter.y+self._selected_node.height > self.scroller.top-1:
        ##     invisible_height = (self.scatter.height - self.scroller.height)  # newscaty is < 0
        ##     if invisible_height<=0:
        ##         return
        ##     print ' node.y=', node_y, ' scatter.y ', self.scatter.y, '  y,top', self.scroller.y, self.scroller.top, ' scat.height', self.scatter.height
        ##     newscaty = self.scroller.center_y - node_y
        ##     sy = - newscaty/ invisible_height # newscaty is always <0
        ##     #print ' newy ', newscaty, '  scroll', self.scroller.scroll_y, '   newysc', sy
        ##     self.scroller.scroll_y = sy

    show_slider = BooleanProperty(False)
    """Hide or show header """

    slider_pos = BooleanProperty(False)
    """The slider is on the left (right) of the tree if True (False) """

class TreeView( TreeViewBase, FloatLayout):
    def __init__(self, **kargs):

        self._topcontainer = self
        self._tree_container = Widget(size_hint=(1,None),pos_hint={'x':0.})
        #self._tree_container = self
        super(TreeView, self).__init__(**kargs)

        self.add_widget(self._tree_container )

        def reposy(*l):
            #print '  ***** reposy ',self._tree_container.height , self.height
            #print '  ***** reposy ',self._tree_container.y , self.y            
            self._tree_container.y = self.top - self._tree_container.height
            self._request_height_layout()
        self._tree_container.bind(height = reposy)        
        self.bind( y = reposy, height = reposy)


class TreeViewWithHeader(TreeViewWithHeaderBase,Widget): # Widget at the end !!
    def __init__(self, **kargs):
        self.debug( ' header  kargs',kargs)
        self._tree_container = Widget()
        self.add_widget(self._tree_container )

        self._topcontainer = self
        self._tree_container.pos = self.pos
        self._tree_container.size = self.size
        super(TreeViewWithHeader, self).__init__(**kargs)
        
        def on_header_resize(header,v):
            self._tree_container.top = self.top - v
        self.header.bind(height=on_header_resize)
        self._tree_container.top = self.top - self.header.height
        


class ScrollableTreeView(ScrollableTreeViewBase,BoxLayout): # Widget at the end !!
    def __init__(self, **kargs):
        self.debug( ' header  kargs',kargs)
        kargs['orientation'] = 'horizontal'
        self._tree_container = Widget(size_hint=(1,None))
        self._scroll_container = self
        self._topcontainer = self

        super(ScrollableTreeView, self).__init__(**kargs)
        
        self.post_init()


class ListViewBase(object): # Widget at the end !!
    def __init__(self, **kargs):
        kargs['indent_level'] = 0
        kargs['indent_start'] = 0
        nc = kargs.setdefault('num_columns',1)
        if nc==0:
            kargs['num_columns'] = 1        
        kargs['hide_root'] = True
        super(ListViewBase, self).__init__(**kargs)

    def append_entries(self, entries):        
        self.entries.extend(entries)
        for e in entries:
            self.add_node(e)
            
    def add_entry(self, e):
        self.entries.append(e)
        return self.add_node(e)

    def remove_entry(self, e):
        i = self.entries.index(e)
        self.remove_entry_at(i)
        
    def remove_entry_at(self,i):
        del(self.entries[i])
        n=self._root.nodes[i]
        self.remove_node(n)

    def select_node_at(self, i):
        n=self._root.nodes[i]
        self.select_node(n)

    def selected_index(self):
        self.debug( self._selected_node)
        self.debug( self._tree_container.children)
        if self._selected_node:
            sn = self._selected_node
            for i,n in enumerate(self._tree_container.children):
                if n==sn:
                    return i                
        return -1


    def select_delta(self, d):
        nodes = self._root.nodes
        if nodes == []:
            return
        i = self.selected_index() +d
        if i < len(nodes) and i>-1:
            self.select_node(nodes[i])
            
    def select_prev(self):
            self.select_delta(-1)
    def select_next(self):
            self.select_delta(1)

    entries = ListProperty([])


    


class ScrollableHeaderTreeView(ScrollableTreeViewBase, TreeViewWithHeaderBase , BoxLayout):
    def __init__(self, **kargs):
        self.debug( ' header  kargs',kargs)
        # define top containers :
        self._tree_container = Widget(size_hint=(1,None))
        self._scroll_container = BoxLayout(size_hint=(1,1),orientation='horizontal')


        self._topcontainer = self
        kargs['orientation'] = 'vertical'
        # init parent classes
        super(ScrollableHeaderTreeView, self).__init__(**kargs)

        
        self.add_widget(self._scroll_container)

        self.debug(' pre-post init        header', self.header.height, self.top) 
        self.debug(' pre-post init       self.widget=',self.width, ' _scroll_container.size=',self._scroll_container.size, self._scroll_container.pos)


        self.post_init()


#class ListView(ListViewBase, ScrollableTreeView): # Widget at the end !!
class MyListView(ListViewBase, ScrollableHeaderTreeView): # Widget at the end !!
    def __init__(self, **kargs):
        kargs.setdefault('hide_header',True)
        super(MyListView, self).__init__(**kargs)
        for e in self.entries:
            self.add_node(e)

    def nodes(self):
        return self._root.nodes
    
class FixedListView(ListViewBase, TreeView): # Widget at the end !!
    def __init__(self, **kargs):
        super(FixedListView, self).__init__(**kargs)
        for e in self.entries:
            self.add_node(e)
        #self.bind(minimum_height = )
        #self.bind(minimum_height = self._tree_trigger_layout)
        

        ## def _top_changed(*l):
        ##     self.debug(' top changed ', l)
        ##     print  'OOOOOOOOOOOO ',self.top , self._tree_container.top, self._tree_container.height , self.minimum_height
        self.bind(top = self._request_height_layout)
        #self.bind(top = _top_changed)

    

from kivy.factory import Factory
for kls in [MyListView, FixedListView]:
    Factory.register(kls.__name__, cls=kls)

if __name__ == '__main__':
    from kivy.app import App

    class TestApp(App):

        def build(self):
            tv = TreeView(hide_root=True)
            add = tv.add_node
            root = add(TreeViewLabel(text='Level 1, entry 1', is_open=True))
            for x in xrange(5):
                add(TreeViewLabel(text='Element %d' % x), root)
            root2 = add(TreeViewLabel(text='Level 1, entry 2', is_open=False))
            for x in xrange(24):
                add(TreeViewLabel(text='Element %d' % x), root2)
            for x in xrange(5):
                add(TreeViewLabel(text='Element %d' % x), root)
            root2 = add(TreeViewLabel(text='Element childs 2', is_open=False),
                        root)
            for x in xrange(24):
                add(TreeViewLabel(text='Element %d' % x), root2)
            return tv
    TestApp().run()
